package com.acgist.snail.downloader;

import java.io.File;

import com.acgist.snail.Snail;
import com.acgist.snail.context.IStatisticsSession;
import com.acgist.snail.context.ITaskSession;
import com.acgist.snail.context.SystemContext;
import com.acgist.snail.gui.GuiContext;
import com.acgist.snail.logger.Logger;
import com.acgist.snail.logger.LoggerFactory;
import com.acgist.snail.net.DownloadException;
import com.acgist.snail.utils.StringUtils;

/**
 * 下载器适配器
 * 
 * @author acgist
 */
public abstract class Downloader implements IDownloader {

    private static final Logger LOGGER = LoggerFactory.getLogger(Downloader.class);
    
    /**
     * 任务失败状态
     */
    protected volatile boolean fail;
    /**
     * 任务完成状态
     */
    protected volatile boolean completed;
    /**
     * 任务信息
     */
    protected final ITaskSession taskSession;
    /**
     * 统计信息
     */
    protected final IStatisticsSession statistics;
    
    /**
     * @param taskSession 任务信息
     */
    protected Downloader(ITaskSession taskSession) {
        // 重置任务下载大小
        taskSession.updateDownloadSize();
        this.fail        = false;
        this.completed   = false;
        this.taskSession = taskSession;
        this.statistics  = taskSession.getStatistics();
    }
    
    /**
     * 验证文件是否完成
     * 
     * @param length       下载数据大小
     * @param downloadSize 累计下载大小
     * @param fileSize     文件大小
     * 
     * @return 是否完成
     */
    public static final boolean checkFinish(int length, long downloadSize, long fileSize) {
        return
            // 没有更多数据
            length < 0 ||
            // 累计下载大小大于文件大小：需要验证文件大小存在没有返回文件大小的情况
            (0L < fileSize && fileSize <= downloadSize);
    }
    
    @Override
    public final String getId() {
        return this.taskSession.getId();
    }
    
    @Override
    public final String getName() {
        return this.taskSession.getName();
    }
    
    @Override
    public final ITaskSession getTaskSession() {
        return this.taskSession;
    }
    
    @Override
    public final boolean statusAwait() {
        return this.taskSession.statusAwait();
    }
    
    @Override
    public final boolean statusDownload() {
        return this.taskSession.statusDownload();
    }
    
    @Override
    public final boolean statusPause() {
        return this.taskSession.statusPause();
    }
    
    @Override
    public final boolean statusCompleted() {
        return this.taskSession.statusCompleted();
    }
    
    @Override
    public final boolean statusFail() {
        return this.taskSession.statusFail();
    }
    
    @Override
    public final boolean statusDelete() {
        return this.taskSession.statusDelete();
    }
    
    @Override
    public final boolean statusRunning() {
        return this.taskSession.statusRunning();
    }
    
    @Override
    public void refresh() throws DownloadException {
    }
    
    @Override
    public boolean verify() throws DownloadException {
        final File downloadFile = this.taskSession.getDownloadFile();
        final boolean verify    = downloadFile.exists();
        if(!verify) {
            LOGGER.info("文件已被删除重置已经下载大小：{}", downloadFile);
            // 如果文件已被删除重置已经下载大小
            this.taskSession.setDownloadSize(0L);
        }
        return verify;
    }
    
    @Override
    public final void fail(String message) {
        // 修改任务状态
        this.taskSession.updateStatus(Status.FAIL);
        // 设置失败信息
        this.fail = true;
        final StringBuilder noticeMessage = new StringBuilder();
        noticeMessage
            .append(this.getName())
            .append("下载失败：");
        if(StringUtils.isEmpty(message)) {
            noticeMessage.append("未知错误");
        } else {
            noticeMessage.append(message);
        }
        GuiContext.getInstance().notice("下载失败", noticeMessage.toString(), GuiContext.MessageType.WARN);
    }
    
    @Override
    public void unlockDownload() {
    }
    
    @Override
    public void release() {
        // GC释放内存
        SystemContext.gc();
        // 注意：任务释放完成解锁（防止提前退出程序导致数据没有保存）
        Snail.getInstance().unlockDownload();
    }

    @Override
    public void delete() {
    }
    
    @Override
    public final void run() {
        final String name = this.getName();
        // 验证任务状态：防止多次点击暂停开始导致阻塞后面下载任务线程
        if(this.statusAwait()) {
            // 加锁：保证资源加载和释放原子性
            synchronized (this.taskSession) {
                // 双重验证
                if(this.statusAwait()) {
                    LOGGER.debug("开始下载任务：{}", name);
                    // 重置状态
                    this.fail      = false;
                    this.completed = false;
                    this.taskSession.setStatus(Status.DOWNLOAD);
                    try {
                        this.open();
                        this.download();
                    } catch (Exception e) {
                        LOGGER.error("任务下载异常", e);
                        this.fail(e.getMessage());
                    }
                    this.checkCompletedAndMark();
                    this.release();
                    // 解除删除锁
                    this.taskSession.unlockDelete();
                    LOGGER.debug("任务下载结束：{}", name);
                } else {
                    LOGGER.debug("任务状态错误：{} - {}", name, this.taskSession.getStatus());
                }
            }
        } else {
            LOGGER.debug("任务状态错误：{} - {}", name, this.taskSession.getStatus());
        }
    }
    
    /**
     * 判断任务是否可以下载：
     * 1.未被标记{@linkplain #fail 失败}
     * 2.未被标记{@linkplain #completed 完成}
     * 3.任务处于{@linkplain #statusDownload() 下载状态}
     * 
     * @return 是否可以下载
     */
    protected final boolean downloadable() {
        return
            !this.fail &&
            !this.completed &&
            this.statusDownload();
    }
    
    /**
     * 检测任务是否完成并且标记
     */
    private final void checkCompletedAndMark() {
        if(this.completed) {
            this.taskSession.updateStatus(Status.COMPLETED);
            GuiContext.getInstance().notice("下载完成", "任务下载完成：" + this.getName());
        }
    }
    
}
